package main

import (
	"context"
	"flag"
	"fmt"
	"math/rand"
	"net"
	"os"
	"os/signal"
	"runtime"
	"strconv"
	"strings"
	"syscall"
	"time"

	"github.com/Terry-Mao/goim/internal/comet"
	"github.com/Terry-Mao/goim/internal/comet/conf"
	"github.com/Terry-Mao/goim/internal/comet/grpc"
	md "github.com/Terry-Mao/goim/internal/logic/model"
	"github.com/Terry-Mao/goim/internal/registry"
	etcdRegistry "github.com/Terry-Mao/goim/internal/registry/etcd"
	log "github.com/golang/glog"
	clientv3 "go.etcd.io/etcd/client/v3"
	"google.golang.org/grpc/resolver"
)

const (
	ver   = "2.0.0"
	appid = "goim.comet"
)

func main() {
	flag.Parse()
	if err := conf.Init(); err != nil {
		panic(err)
	}
	rand.Seed(time.Now().UTC().UnixNano())
	runtime.GOMAXPROCS(runtime.NumCPU())
	println(conf.Conf.Debug)
	log.Infof("goim-comet [version: %s env: %+v] start", ver, conf.Conf.Env)

	// etcd 注册中心
	cli, err := clientv3.New(clientv3.Config{
		Endpoints:   []string{"192.168.34.4:2379"},
		DialTimeout: 5 * time.Second,
	})
	if err != nil {
		panic(err)
	}
	rs, err := etcdRegistry.NewRegistry(cli)
	if err != nil {
		panic(err)
	}

	// name resolver
	grpcResolver, err := comet.NewGrpcResolverBuilder(rs, time.Duration(conf.Conf.RPCClient.Timeout))
	if err != nil {
		panic(err)
	}
	resolver.Register(grpcResolver)

	// new comet server
	srv := comet.NewServer(conf.Conf)
	if err := comet.InitWhitelist(conf.Conf.Whitelist); err != nil {
		panic(err)
	}
	if err := comet.InitTCP(srv, conf.Conf.TCP.Bind, runtime.NumCPU()); err != nil {
		panic(err)
	}
	if err := comet.InitWebsocket(srv, conf.Conf.Websocket.Bind, runtime.NumCPU()); err != nil {
		panic(err)
	}
	if conf.Conf.Websocket.TLSOpen {
		if err := comet.InitWebsocketWithTLS(srv, conf.Conf.Websocket.TLSBind, conf.Conf.Websocket.CertFile, conf.Conf.Websocket.PrivateFile, runtime.NumCPU()); err != nil {
			panic(err)
		}
	}

	// 构造并启动 comet 的 grpc 服务端，
	rpcSrv := grpc.New(conf.Conf.RPCServer, srv)
	// 注册到注册中心，并定时向注册中心提交统计数据
	ins, cancel := register(rs, srv)

	// signal
	c := make(chan os.Signal, 1)
	signal.Notify(c, syscall.SIGHUP, syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT)
	for {
		s := <-c
		log.Infof("goim-comet get a signal %s", s.String())
		switch s {
		case syscall.SIGQUIT, syscall.SIGTERM, syscall.SIGINT:
			if cancel != nil {
				cancel()
			}
			rs.UnRegister(context.Background(), ins)
			rs.Close()
			rpcSrv.GracefulStop()
			srv.Close()
			log.Infof("goim-comet [version: %s] exit", ver)
			log.Flush()
			return
		case syscall.SIGHUP:
		default:
			return
		}
	}
}

func register(rs registry.Registry, srv *comet.Server) (*registry.ServiceInstance, context.CancelFunc) {
	env := conf.Conf.Env
	// addr := ip.InternalIP()
	addr := "192.168.34.4"
	_, port, _ := net.SplitHostPort(conf.Conf.RPCServer.Addr)
	ins := &registry.ServiceInstance{
		Region:   env.Region,
		Zone:     env.Zone,
		Env:      env.DeployEnv,
		Hostname: env.Host,
		AppID:    appid,
		Addrs: []string{
			"grpc://" + addr + ":" + port,
		},
		Metadata: map[string]string{
			md.MetaWeight:  strconv.FormatInt(env.Weight, 10),
			md.MetaOffline: strconv.FormatBool(env.Offline),
			md.MetaAddrs:   strings.Join(env.Addrs, ","),
		},
	}

	ctx, cancel := context.WithCancel(context.Background())
	err := rs.Register(ctx, ins)
	if err != nil {
		panic(err)
	}

	// 定时统计更新注册中心里的 comet 上的连接和 ip 数量
	go func() {
		for {
			select {
			case <-ctx.Done():
				return
			default:
				var (
					err   error
					conns int
					ips   = make(map[string]struct{})
				)
				for _, bucket := range srv.Buckets() {
					for ip := range bucket.IPCount() {
						ips[ip] = struct{}{}
					}
					conns += bucket.ChannelCount()
				}
				ins.Metadata[md.MetaConnCount] = fmt.Sprint(conns)
				ins.Metadata[md.MetaIPCount] = fmt.Sprint(len(ips))
				if err = rs.Register(ctx, ins); err != nil {
					log.Errorf("dis.Set(%+v) error(%v)", ins, err)
					time.Sleep(time.Second)
					continue
				}
				time.Sleep(time.Second * 10)
			}
		}
	}()
	return ins, cancel
}
